﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;

namespace XNALibrary.ZCAMERA
{
    public class ZCameraThirdPerson : ZCamera
    {
        public ZCameraThirdPerson(InputHandler ih, IWatchable iw)
            : base(ih, iw)
        {
        }

        public override Vector3 OffsetDir { get { return -1 * Target.GetDirection() + Target.GetUp()/1.5f; } }
        public override void Update(GameTime gt)
        {
             GamePadState gps = input.GamePads[0];
            KeyboardHandler kbh = input.KeyboardHandler;

            //Modify WatchDistance
            #if XBOX360
                WatchDistance -= gps.ThumbSticks.Right.Y * Sensitivity;
                if(WatchDistance < 3) 
                    WatchDistance = 3;
                else if(WatchDistance > 1000)
                    WatchDistance = 1000;

            #else
                if (kbh.IsKeyDown(Keys.Z))
                {
                    WatchDistance += Sensitivity;
                    Console.WriteLine("WatchDistance: " + WatchDistance);
                    if (WatchDistance > 1000)
                        WatchDistance = 1000;
                }
                if (kbh.IsKeyDown(Keys.X))
                {
                    WatchDistance -= Sensitivity;
                    if (WatchDistance < 3)
                        WatchDistance = 3;
                    Console.WriteLine("WatchDistance: " + WatchDistance);
                }
            #endif
        }

        public override string GetViewName()
        {
             return "Third Person \"Over the Shoulder\" View";
        }

        public override string GetGuide()
        {
            return "use Z to zoom in and X to zoom out";
        }
    }

    public class ZCamera : ICamera
    {
        public float WatchDistance;
        public float Sensitivity;
        public float RotationDegree;
        public float PitchDegree;
        public Vector3 PitchAxis { get; private set; }

        public IWatchable Target;

        public virtual Vector3 Forward { get { return Vector3.UnitX; } }
        public Vector3 UpVector
        {
            get
            {
                //vector math to calculate Cam Up
                Vector3 horizontal = Vector3.Cross(OffsetDir, Vector3.Up);
                return Vector3.Cross(horizontal, OffsetDir);
            }
        }

        public Vector3 CamPosition
        {
            get
            {
                Matrix rot = Matrix.CreateFromYawPitchRoll(MathHelper.ToRadians(RotationDegree), MathHelper.ToRadians(PitchDegree), 0);
                Vector3 offset = Vector3.Transform(Target.GetDirection(), rot);
                offset.Normalize();
                return Target.GetPosition() + offset * WatchDistance;

                /*
                Vector3 offvect = Vector3.Transform(OffsetDir,
                                         Matrix.CreateRotationY(MathHelper.ToRadians(RotationDegree)) * Matrix.CreateFromAxisAngle(PitchAxis, MathHelper.ToRadians(YawDegree)));
                Vector3 offset = WatchDistance * offvect;
                return Target.GetPosition() + offset;
                */ 
            }
        }

        public virtual Vector3 OffsetDir 
        { 
            get
            {
                return CamPosition - Target.GetPosition();
            } 
        }
        protected float fieldOfView;
        protected float aspectRatio;
        protected float nearPlane;
        protected float farPlane;

        protected InputHandler input;

        public ZCamera(InputHandler ih, IWatchable iw)
        {
            Target = iw;
            fieldOfView = MathHelper.ToRadians(45.0f);
            aspectRatio = 1.0f;
            nearPlane = 1.0f;
            farPlane = 10000.0f;

            input = ih;

            WatchDistance = 500;
            Sensitivity = 3;

            PitchDegree = 30;
            PitchAxis = Vector3.Cross(Target.GetDirection(), Target.GetUp());
        }

        #region ICamera Members

        public Matrix View
        {
            get
            {
                return Matrix.CreateLookAt(CamPosition, Target.GetPosition(), UpVector);
            }
        }

        public Matrix Projection
        {
            get
            {
                return Matrix.CreatePerspectiveFieldOfView(fieldOfView, aspectRatio,
                        nearPlane, farPlane);
            }
        }

        public virtual void Update(GameTime gt)
        {
            
            GamePadState gps = input.GamePads[0];
            KeyboardHandler kbh = input.KeyboardHandler;

            //Modify WatchDistance
            #if XBOX360
                WatchDistance -= gps.ThumbSticks.Right.Y * Sensitivity;
                if(WatchDistance < 3) 
                    WatchDistance = 3;
                else if(WatchDistance > 1000)
                    WatchDistance = 1000;
                RotationDegree += gps.ThumbSticks.Right.X * Sensitivity;
                if (RotationDegree >= 360 || RotationDegree <= -360)
                    RotationDegree = 0;

            #else
                if (kbh.IsKeyDown(Keys.X))
                {
                    WatchDistance += Sensitivity;
                    Console.WriteLine("WatchDistance: " + WatchDistance);
                    if (WatchDistance > 1000)
                        WatchDistance = 1000;
                }
                if (kbh.IsKeyDown(Keys.Z))
                {
                    WatchDistance -= Sensitivity;
                    if (WatchDistance < 3)
                        WatchDistance = 3;
                    Console.WriteLine("WatchDistance: " + WatchDistance);
                }


                //apply circular rotation to camera
                if(kbh.IsKeyDown(Keys.A))
                {
                    RotationDegree -= Sensitivity;
                    Console.WriteLine("Rotation: " + RotationDegree);
                }
                if(kbh.IsKeyDown(Keys.S))
                {
                    RotationDegree += Sensitivity; 
                    Console.WriteLine("Rotation: " + RotationDegree);
                }
                if (RotationDegree >= 180)
                    RotationDegree = -179;
                if (RotationDegree <= -180)
                    RotationDegree = 179;   


                //apply Yaw rotation
                if(kbh.IsKeyDown(Keys.D))
                {
                    PitchDegree += Sensitivity;
                    Console.WriteLine("Yaw: " + PitchDegree);
                }
                if (kbh.IsKeyDown(Keys.C))
                {
                    PitchDegree -= Sensitivity;
                    Console.WriteLine("Yaw: " + PitchDegree);
                }
                if (PitchDegree >= 90)
                    PitchDegree = 89;
                if (PitchDegree <= -90)
                    PitchDegree = -89;   
               
            #endif
        }

        public virtual string GetViewName()
        {
            return "UserControl View";
        }

        public Vector3 GetPosition()
        {
            return CamPosition;
        }

        public virtual string GetGuide()
        {
            return "use Z to zoom in and X to zoom out \n use A and S to rotate the camera around the target";
        }
        #endregion
    }
}
